package com.gavin.lingxi.rtc.call;

import android.Manifest;
import android.annotation.TargetApi;
import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

import androidx.constraintlayout.widget.ConstraintLayout;

import com.gavin.lingxi.RTCParam;
import com.gavin.lingxi.WertcSdk;
import com.gavin.lingxi.base.AppBaseActivity;
import com.gavin.lingxi.common.utils.NetSwitchoverUtil;
import com.gavin.lingxi.rtc.R;
import com.gavin.lingxi.rtc.R2;
import com.gavin.lingxi.rtc.util.BeepManager;
import com.gavin.lingxi.rtclib.IAppRTCClient;
import com.gavin.lingxi.rtclib.LiveRtcService;
import com.gavin.lingxi.rtclib.Options;
import com.gavin.lingxi.rtclib.WebRtcStats;
import com.puppy.bingo.tools.ThreadUtils;
import com.puppy.bingo.tools.ToastUtils;
import com.tbruyelle.rxpermissions2.RxPermissions;

import org.json.JSONException;
import org.json.JSONObject;
import org.mym.plog.PLog;
import org.webrtc.Camera1Enumerator;
import org.webrtc.CameraEnumerator;
import org.webrtc.DataChannel;
import org.webrtc.EglBase;
import org.webrtc.Logging;
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import org.webrtc.VideoCapturer;
import org.webrtc.VideoFileRenderer;
import org.webrtc.VideoFrame;
import org.webrtc.VideoSink;

import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;

import butterknife.BindView;
import butterknife.OnClick;

import static com.puppy.bingo.tools.ThreadUtils.post;


/**
 * Created by xiangfeng on 17/11/12.
 */

public class LiveActivity extends AppBaseActivity {

    private static final String TAG = LiveActivity.class.getSimpleName();
    private BeepManager mBeepManager;

    private static class LocalProxyVideoSink implements VideoSink {
        private VideoSink target;

        @Override
        synchronized public void onFrame(VideoFrame frame) {
            if (target == null) {
                Logging.d(TAG, "Dropping frame in proxy because target is null.");
                return;
            }
            target.onFrame(frame);
        }

        synchronized public void setTarget(VideoSink target) {
            this.target = target;
        }
    }

    private static class ProxyVideoSink implements VideoSink {
        private VideoSink target;

        @Override
        synchronized public void onFrame(VideoFrame frame) {
//            VideoFrame rotationFrame = new VideoFrame(frame.getBuffer(), 180, frame.getTimestampNs());
            if (target == null) {
                Logging.d(TAG, "Dropping frame in proxy because target is null.");
                return;
            }
            target.onFrame(frame);
        }

        synchronized public void setTarget(VideoSink target) {
            this.target = target;
        }
    }


    private final ProxyVideoSink remoteProxyRenderer = new ProxyVideoSink();
    private final LocalProxyVideoSink localProxyRenderer = new LocalProxyVideoSink();
    private EglBase rootEglBase;

    private VideoFileRenderer videoFileRenderer;

    private final List<VideoSink> remoteRenderers = new ArrayList<>();



    private boolean iceConnected;
    private long mLastTime;
    private long mIceConnectedCostTime;
    private int mType = RTCParam.TYPE_CALLER;
    private String mUserId;
    private RxPermissions mRxPermissions;


    @BindView(R2.id.pip_video_view)
    SurfaceViewRenderer pipRenderer;
    @BindView(R2.id.fullscreen_video_view)
    SurfaceViewRenderer fullscreenRenderer;
    @BindView(R2.id.hungup_btn)
    LinearLayout mHungupBtn;
    @BindView(R2.id.switch_camera_btn)
    LinearLayout mSwitchCameraBtn;
    @BindView(R2.id.mute_btn)
    TextView mMuteVoiceBtn;
    @BindView(R2.id.live_right_bar_container)
    View mLiveRightBar;
    @BindView(R2.id.view_tips)
    TextView mTipsView;
    @BindView(R2.id.call_layout)
    ConstraintLayout constraintLayout;


    private boolean mInitData = false;
    private boolean mIsSwappedFeeds;

    private LiveRtcService mLiveRtcService;
    private boolean mHasObstacle = false;
    private HandlerThread mHandlerThread = new HandlerThread(LiveActivity.class.getSimpleName());
    private ConnectTimeOut mConnectTimeout = new ConnectTimeOut(this);
    private String mapId;

    @OnClick({R2.id.hungup_btn, R2.id.switch_camera_btn, R2.id.mute_btn,
            R2.id.pip_video_view, R2.id.tv_recieve,R2.id.tv_refuse})
    public void onClick(View view) {
        int viewId = view.getId();
        if (viewId == R.id.hungup_btn) {
            mLiveRtcService.sendBye();
            finish();
        } else if (viewId == R.id.switch_camera_btn) {
            if (mLiveRtcService != null) {
                mLiveRtcService.switchCamera();
            }
        } else if (viewId == R.id.mute_btn) {
            view.setSelected(!view.isSelected());
            boolean silence = view.isSelected();
            if (mLiveRtcService != null) {
                mLiveRtcService.setAudioEnabled(!silence);
            }
        } else if (viewId == R.id.pip_video_view) {
            setSwappedFeeds(!mIsSwappedFeeds);
        }  else if(viewId == R.id.tv_recieve){
            recieveCall();
        }else if(viewId == R.id.tv_refuse){
            finish();
        }
    }



    private void recieveCall() {
        if (mLiveRtcService != null) {
            mLiveRtcService.sendEnsureJoin();
        }
        changeScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
        mBeepManager.close();
        constraintLayout.setVisibility(View.GONE);
    }


    @TargetApi(19)
    private static int getSystemUiVisibility() {
        int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
            flags |= View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY;
        }
        return flags;
    }


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN | WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD | WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
        getWindow().getDecorView().setSystemUiVisibility(getSystemUiVisibility());
        mLastTime = System.currentTimeMillis();
        super.onCreate(savedInstanceState);
        mType = getIntent().getIntExtra(RTCParam.INTENT_CALL_TYPE,RTCParam.TYPE_CALLER);
        mUserId = getIntent().getStringExtra(RTCParam.INTENT_USER_ID);
        //系统版本在6.0之下，需要动态获取权限。
        initPermissions();
        init();
    }

    private void initPermissions() {
        mRxPermissions = new RxPermissions(this);
        mRxPermissions.requestEach(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
                .all(permission -> {
                    if (permission.granted) {
                        return true;
                    } else {
                        if (Manifest.permission.CAMERA.equals(permission.name)) {
                            ToastUtils.show(LiveActivity.this, R.string.open_camera_permission);
                            finish();
                        } else if (Manifest.permission.RECORD_AUDIO.equals(permission.name)) {
                            ToastUtils.show(LiveActivity.this, R.string.open_audio_permission);
                            finish();
                        }
                    }
                    return false;
                }).subscribe(allGranted -> {
            if (allGranted) {

            }
        });
    }

    @Override
    public int getLayoutId() {
        return R.layout.activity_live;
    }

    @Override
    public boolean needButterKnife() {
        return true;
    }

    private void init() {
        initUI();
        initRtcService();
        startJoin();
    }

    private void initRtcService(){
        iceConnected = false;
        Intent intent = getIntent();
        if (intent != null) {
        }
        if (mHandlerThread != null) {
            mHandlerThread.start();
        }


        Options options = Options.builder()
                .setInitiator(RTCParam.TYPE_CALLER == mType)
                .setVideoEnable(true)
                .setAudioEnable(true)
                .setUid(WertcSdk.getInstance().getUserId())
                .setIceServerList(NetSwitchoverUtil.getIceServer())
                .setRtcMsgClient(WertcSdk.getInstance())
                .createOptions();

        mLiveRtcService = new LiveRtcService(getApplicationContext(), options);
        mLiveRtcService.setOnLiveRtcServiceListener(mListener);
        VideoCapturer videoCapturer = createVideoCapturer();
        mLiveRtcService.initPeerConnection(getApplicationContext(), rootEglBase);
        mLiveRtcService.createPeerConnection(localProxyRenderer, remoteRenderers, videoCapturer);
    }

    private LiveRtcService.LiveRtcServiceListener mListener = new LiveRtcService.LiveRtcServiceListener() {
        @Override
        public void onIceConnected() {
            PLog.d("[rtc] onIceConnected");
            iceConnected = true;
            ThreadUtils.post(new Runnable() {
                @Override
                public void run() {
                    PLog.i("onIceConnected dismissLoadingDialog");
                }
            });
            mConnectTimeout.stop();

        }

        @Override
        public void onIceDisconnected() {
            PLog.e("[rtc] onIceDisconnected");
        }

        @Override
        public void onPeerConnectionClosed() {
            PLog.e("[rtc] onPeerConnectionClosed");
            if (isFinishing()) {
                return;
            }
            mConnectTimeout.stop();
        }

        @Override
        public void onDataChannel(final DataChannel dataChannel) {
            PLog.d("[rtc] onDataChannel: " + dataChannel.state());
            dataChannel.label();

            if (!mInitData) {


                mInitData = true;
            }
        }

        @Override
        public void recvOffer() {
            PLog.d("[rtc] recvOffer");
            if (isFinishing()) {
                return;
            }

            long curTime = System.currentTimeMillis();
            mIceConnectedCostTime = (int) (curTime - mLastTime);
            mLastTime = curTime;
            PLog.d("onIceConnected, cost: " + mIceConnectedCostTime);


            post(new Runnable() {
                @Override
                public void run() {
                    PLog.i(TAG, "ice connected");
                    callConnected();
                }
            });
        }

        @Override
        public void onBusy() {
            PLog.d("[rtc] onBusy");
            ThreadUtils.post(new Runnable() {
                @Override
                public void run() {
                    finish();
                }
            });

        }

        @Override
        public void onOffLine() {
            PLog.d("[rtc] onOffLine");
            ThreadUtils.post(new Runnable() {
                @Override
                public void run() {
                    ToastUtils.show(LiveActivity.this, R.string.bingo_offline, Toast.LENGTH_LONG);
                    finish();
                }
            });

        }

        @Override
        public void onPeerConnectionError(String errorMessage,int errorType) {
            PLog.d("[rtc] onPeerConnectionError");
            finish();
            ThreadUtils.post(new Runnable() {
                @Override
                public void run() {
                    if(errorType == IAppRTCClient.AUDIO_INIT_ERROR){
                        ToastUtils.show(LiveActivity.this, R.string.audio_init_fail);
                    }else {
                        ToastUtils.show(LiveActivity.this, R.string.connect_error);
                    }
                    mConnectTimeout.stop();
                }
            });
        }

        @Override
        public void onBye() {
            finish();
        }

        @Override
        public void onStatsReady(WebRtcStats stats) {

        }
    };


    @Override
    public void onPause() {
        super.onPause();

        PLog.d("onPause");
        if (getWindow() != null) {
            getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }

        if (mLiveRtcService != null) {
            mLiveRtcService.onPause();
        }
        pauseVideo();
    }

    @Override
    public void onResume() {
        super.onResume();

        if (getWindow() != null) {
            getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
        }
        if (mLiveRtcService != null) {
            mLiveRtcService.onResume();
        }
        resumeVideo();
    }

    @Override
    protected void onDestroy() {
        remoteProxyRenderer.setTarget(null);
        localProxyRenderer.setTarget(null);
        if (mLiveRtcService != null) {
            mLiveRtcService.release();
            mLiveRtcService = null;
        }
        if (pipRenderer != null) {
            pipRenderer.release();
            pipRenderer = null;
        }
        if (videoFileRenderer != null) {
            videoFileRenderer.release();
            videoFileRenderer = null;
        }
        if (fullscreenRenderer != null) {
            fullscreenRenderer.release();
            fullscreenRenderer = null;
        }
        if (iceConnected) {
            setResult(RESULT_OK);
        } else {
            setResult(RESULT_CANCELED);
        }
        if(mBeepManager!=null){
            mBeepManager.close();
        }
        if(mHandlerThread != null){
            mHandlerThread.quit();
            mHandlerThread = null;
        }
        mConnectTimeout.stop();
        super.onDestroy();

    }

    private void initUI() {
        initVideoView();
        initCallLayout();
    }

    private void initCallLayout() {

        if(mType == RTCParam.TYPE_CALLER){
            changeScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
            constraintLayout.setVisibility(View.GONE);
        }else {
            constraintLayout.setVisibility(View.VISIBLE);
            mBeepManager = new BeepManager(this);
            mBeepManager.setMediaResId(R.raw.ring_new);
            mBeepManager.playBeepSoundAndVibrate();
            changeScreenOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
        }

    }

    private void initVideoView() {
        remoteRenderers.add(remoteProxyRenderer);
        rootEglBase = EglBase.create();
        pipRenderer.init(rootEglBase.getEglBaseContext(), null);
        pipRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT);
        fullscreenRenderer.init(rootEglBase.getEglBaseContext(), null);
        fullscreenRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
        pipRenderer.setZOrderMediaOverlay(true);
        pipRenderer.setEnableHardwareScaler(true);
        fullscreenRenderer.setEnableHardwareScaler(true);
        mMuteVoiceBtn.setSelected(true);
        setSwappedFeeds(false);
    }


    private VideoCapturer createVideoCapturer() {
        VideoCapturer videoCapturer = null;
        if (videoCapturer == null) {
            videoCapturer = createCameraCapturer(new Camera1Enumerator(true));
        }
        if (videoCapturer == null) {
            PLog.e("Failed to open camera");
            return null;
        }
        return videoCapturer;
    }


    private VideoCapturer createCameraCapturer(CameraEnumerator enumerator) {
        final String[] deviceNames = enumerator.getDeviceNames();

        // First, try to find front facing camera
        PLog.d("Looking for front facing cameras.");
        for (String deviceName : deviceNames) {
            if (enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating front facing camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        // Front facing camera not found, try something else
        PLog.d("Looking for other cameras.");
        for (String deviceName : deviceNames) {
            if (!enumerator.isFrontFacing(deviceName)) {
                Logging.d(TAG, "Creating other camera capturer.");
                VideoCapturer videoCapturer = enumerator.createCapturer(deviceName, null);

                if (videoCapturer != null) {
                    return videoCapturer;
                }
            }
        }

        return null;
    }

    private void setSwappedFeeds(boolean isSwappedFeeds) {
        PLog.d("setSwappedFeeds: " + isSwappedFeeds);
        mIsSwappedFeeds = isSwappedFeeds;
        localProxyRenderer.setTarget(isSwappedFeeds ? fullscreenRenderer : pipRenderer);
        remoteProxyRenderer.setTarget(isSwappedFeeds ? pipRenderer : fullscreenRenderer);
        fullscreenRenderer.setMirror(isSwappedFeeds);
        pipRenderer.setMirror(!isSwappedFeeds);
    }




    private void callConnected() {
        if (mLiveRtcService != null) {
            mLiveRtcService.enableStatsEvents();
        }
        setSwappedFeeds(false /* isSwappedFeeds */);
    }


    /**
     * 连接超时计时器
     */
    static class ConnectTimeOut extends Handler {

        static final int TIME_OUT_MILLIS = 20 * 1000;
        static final int MSG_TIME_OUT = 1;

        boolean isStart = false;

        private WeakReference<LiveActivity> target;

        public ConnectTimeOut(LiveActivity context) {
            target = new WeakReference<>(context);
        }

        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
                case MSG_TIME_OUT:
                    if (target.get() != null && !target.get().isFinishing()) {
                        ToastUtils.show(target.get(), R.string.connect_time_out, Toast.LENGTH_SHORT);
                        target.get().finish();
                    }
                    break;
                default:
                    break;
            }
        }

        public void start() {
            if (!isStart) {
                isStart = true;
                sendEmptyMessageDelayed(MSG_TIME_OUT, TIME_OUT_MILLIS);
            }
        }

        public void stop() {
            removeMessages(MSG_TIME_OUT);
            isStart = false;
        }
    }

    private void startJoin() {
        if(mType != RTCParam.TYPE_CALLER){
            return;
        }

        mConnectTimeout.start();

        JSONObject jsonObject = new JSONObject();
        try {
            jsonObject.put("type","joined");
            jsonObject.put("uid", mUserId);
        } catch (JSONException e) {
            e.printStackTrace();
        }
        WertcSdk.getInstance().sendRTCMsg(jsonObject);
        long curTime = System.currentTimeMillis();
        mLastTime = curTime;
    }


    private void pauseVideo() {
        if (pipRenderer != null) {
            pipRenderer.pauseVideo();
        }
        if (fullscreenRenderer != null) {
            fullscreenRenderer.pauseVideo();
        }
    }

    private void resumeVideo() {
        if (pipRenderer != null) {
            pipRenderer.disableFpsReduction();
        }
        if (fullscreenRenderer != null) {
            fullscreenRenderer.disableFpsReduction();
        }
    }


    Runnable clearTipsRunable = new Runnable() {
        @Override
        public void run() {
            if (mTipsView != null) {
                mTipsView.setText("");
            }
        }
    };

    public void setTipsContext(String context) {
        ThreadUtils.removeCallbacks(clearTipsRunable);
        if (mTipsView != null) {
            mTipsView.setText(context);
            ThreadUtils.postDelayed(clearTipsRunable, 2000);
        }
    }



    /**
     * 横竖屏切换
     */
    private void changeScreenOrientation(int orientation) {
            setRequestedOrientation(orientation);
    }
}
